# 01 Rust快速入门 ## 基本语法 ### 基本数据类型 Rust 是一门静态类型语言,变量默认是不可变的(immutable)。 * **标量类型**: * 整型:`i8`, `i32`, `i64`, `u32`(有符号与无符号) * 浮点型:`f32`, `f64` * 布尔型:`bool`(`true` / `false`) * 字符型:`char`(表示 Unicode 标量值,占 4 个字节) * **复合类型**: * 元组(Tuple):`(i32, f64, &str)`,长度固定,类型可不同。 * 数组(Array):`[i32; 3]`,长度固定,类型必须相同。 * 动态数组(Vec): `Vec[i32]` 长度不固定,可以追加; ```rust let x: i32 = 42; // 明确指定类型 let pi = 3.14; // 自动推导为 f64 let is_active: bool = true; let tup: (i32, f64) = (500, 6.4); let arr: [i32; 3] = [1, 2, 3]; ``` **String基础操作**: ```rust fn main() { // 1. 初始化 let mut s: String = String::from("Hello"); let s_slice: &str = "World"; // 2. 拼接 // push_str 追加切片,push 追加字符,+ 操作符(注意所有权转移) s.push_str(", "); s.push('R'); let combined = s + s_slice; // 此时 s 的所有权已被转移 let s2 = format!("{} {}", "Hello", "Rust"); // 使用 format! 宏(不转移所有权) // 3. 查找 let text = "rust programming"; let pos = text.find("gram"); // 返回 Option let contains_rust = text.contains("rust"); // 返回 bool // 4. 大小写转换 let upper = "hello".to_uppercase(); let lower = "WORLD".to_lowercase(); // 5. 相等比较 let is_equal = ("abc" == "abc"); // 6. 长度计算(注意:计算的是字节数,不是字符数) let len = "Hello".len(); let char_count = "你好".chars().count(); // 获取真实字符数 // 7. 遍历 for c in "rust".chars() { print!("{} ", c); } // 8. 转码 (UTF-8 字节与字符串互转) let bytes: &[u8] = text.as_bytes(); let decoded = String::from_utf8(bytes.to_vec()).unwrap(); } ``` **数组与动态数组 (Array & Vec)**: ```rust fn main() { // --- 固定大小数组 (Array) --- // 1. 初始化 let arr: [i32; 3] = [1, 2, 3]; let zeros = [0; 5]; // 初始化为 5 个 0 // 2. 遍历 for x in arr.iter() { println!("{}", x); } // --- 动态数组 (Vec) --- // 1. 初始化 let mut vec: Vec = Vec::new(); let mut vec_macro = vec![1, 2, 3]; // 2. 插入与修改 vec_macro.push(4); // 末尾添加 vec_macro.insert(0, 99); // 指定索引插入(开销较大) // 3. 删除 let last = vec_macro.pop(); // 弹出末尾元素 // 4. 查找与包含 let has_two = vec_macro.contains(&2); let index = vec_macro.iter().position(|&x| x == 3); // 查找索引 // 5. 长度计算 let len = vec_macro.len(); let is_empty = vec_macro.is_empty(); // 6. 遍历 for item in &vec_macro { println!("{}", item); } } ``` **键值对映射 (HashMap)**: ```rust use std::collections::HashMap; fn main() { // 1. 初始化 let mut scores = HashMap::new(); // 2. 插入 scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50); // entry API: 键不存在时才插入 scores.entry(String::from("Green")).or_insert(30); // 3. 查找 let score = scores.get("Blue"); // 返回 Option<&V> // 4. 遍历 for (key, value) in &scores { println!("{}: {}", key, value); } // 5. 删除与长度 scores.remove("Yellow"); let count = scores.len(); } ``` **基本数据类型常用操作 (整型、布尔等)**: ```rust fn main() { // 1. 类型转换 (as 关键字) let a: i32 = 10; let b: f64 = a as f64; // 2. 基础数学运算与溢出检查方法 let x: u32 = 100; let checked_add = x.checked_add(1); // 返回 Option,防止溢出 let wrapping_add = x.wrapping_add(1); // 溢出时回绕 // 3. 数值与字符串互转 let num_str = "42"; let parsed_num: i32 = num_str.parse().unwrap(); // 字符串转数字 let back_to_str = parsed_num.to_string(); // 数字转字符串 } ``` ### 定义函数 使用 `fn` 关键字。函数签名必须明确声明参数类型;如果函数有返回值,必须在箭头 `->` 后指定返回类型。函数体的最后一个表达式会自动作为返回值返回(无需写 `return` 和分号)。 ```rust fn add(a: i32, b: i32) -> i32 { a + b // 隐式返回,注意没有分号 } ``` ### 定义结构体和方法 使用 `struct` 定义自定义数据结构,并通过 `impl` 块为其绑定关联函数和方法。 ```rust #[derive(Debug)] struct Rectangle { width: u32, height: u32, } impl Rectangle { // 关联函数(类似于其他语言的静态方法/构造函数) fn new(width: u32, height: u32) -> Rectangle { Rectangle { width, height } } // 方法(以 &self 作为第一个参数) fn area(&self) -> u32 { self.width * self.height } } ``` ### 定义宏 ### 定义枚举值 Rust 的枚举(Enum)极其强大,不仅可以作为简单的状态标记,还可以携带各种类型的数据(元组或结构体样式)。 ```rust enum Message { Quit, // 纯状态标记 Move { x: i32, y: i32 }, // 具名字段结构体样式 Write(String), // 携带数据的元组样式 } ``` ### 定义异常值 Rust **没有传统意义上的 `try-catch` 异常抛出机制**。错误被视为普通的返回值,主要通过两种类型处理: 1. **`Result`**:用于处理可能失败的操作。包含成功 `Ok(T)` 和失败 `Err(E)`。 2. **`Option`**:用于处理可能存在或不存在的值。包含有值 `Some(T)` 和无值 `None`。 ```rust // 自定义错误枚举 #[derive(Debug)] enum MathError { NegativeSquareRoot(f64), // 携带错误现场数据的枚举成员 } // 定义一个返回 Result 的“可能出错”的函数 fn safe_sqrt(number: f64) -> Result { if number < 0.0 { return Err(MathError::NegativeSquareRoot(number)); // 抛出“异常”值 } Ok(number.sqrt()) } // 定义一个返回 Option 的“可能为空”的函数 fn get_user_status(id: u64) -> Option { if id == 1 { Some("Active".to_string()) // 有值时必须用 Some 包装 } else { None // 无值时返回 None } } ``` ## 语言特征 ### 数据的归属借还机制 #### 默认的数据是不可修改 只能引用可读 Rust 的核心设计哲学是**内存安全与并发安全**。 * **默认不可变**:在 Rust 中,变量默认是不可变的(immutable)。一旦声明并绑定了数值,你就无法修改它。 * **只读引用(`&T`)**:你可以通过借用(Borrowing)的方式获取数据的只读引用。在同一时间,你可以拥有无数个只读引用,但没有任何人可以修改这块数据。 ```rust let s = String::from("hello"); let r1 = &s; // 只读引用 1 let r2 = &s; // 只读引用 2 println!("{} and {}", r1, r2); // 合法:大家一起读 ``` #### `&mut` 表数据借还可写 * **可变引用(`&mut T`)**:如果你想修改借用的数据,必须声明为可变借用。 * **铁律(排他性)**:Rust 的借用检查器(Borrow Checker)规定:**在任意给定的时间,要么只能有任意数量的只读引用(`&T`),要么只能有且仅有一个可变引用(`&mut T`)**。这从根本上消除了多线程或复杂逻辑下的数据竞争(Data Race)。 ```rust let mut s = String::from("hello"); let r1 = &mut s; // 可变借用 r1.push_str(", world"); println!("{}", r1); // 合法:当前只有 r1 在操作 ``` #### 和 C++ 的智能指针对应理解 Rust 的所有权和借用机制,本质上是把 C++ 中靠人工规范和复杂工具去防范的内存错误,直接在编译期通过类型系统规范化了: * **`Box` 对应 C++ 的 `std::unique_ptr**`:独占式堆内存所有权,离开作用域自动释放。 * **`Rc / Arc` 对应 C++ 的 `std::shared_ptr**`:引用计数智能指针。`Rc` 用于单线程,`Arc` 用于多线程(Atomic)。 * **`&` 和 `&mut` 对应 C++ 的 `const T&` 和 `T&**`:但 Rust 的引用带有严格的生命周期和编译期借用检查,杜绝了 C++ 中常见的悬垂引用(Dangling Reference)。 #### 以及其他的数据生命周期相关 * **生命周期(Lifetimes, `'a`)**:这是 Rust 最独特的高级概念。生命周期并不是指变量活多久,而是**编译器用来确保引用有效性的作用域范围**。 * **生命周期标注**:当函数返回一个引用时,编译器需要知道这个引用的存活时间是否和输入的参数挂钩。如果不标注,编译器无法推导。 ```rust // 告诉编译器:返回值引用的生命周期,和参数 x、y 中较短的那一个保持一致 fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } ``` --- ### 模式匹配 #### 常见的数据包装 Rust 没有像其他语言那样隐式的空指针或非结构化异常,而是通过精妙的泛型枚举来包裹数据: * **`Option`**:处理“有值”或“无值”的场景。包含 `Some(T)`(有值)和 `None`(无值)。 * **`Result`**:处理“成功”或“失败”的场景。包含 `Ok(T)`(成功结果)和 `Err(E)`(错误信息)。 * **自定义枚举包装**:例如前面提到的携带数据的枚举 `MathError::NegativeSquareRoot(f64)`。 #### 数据模式匹配和解包 模式匹配不仅是条件分支,更是强大的**数据解构工具**。 ```rust enum Message { Quit, Move { x: i32, y: i32 }, Write(String), } fn handle_message(msg: Message) { match msg { Message::Quit => println!("退出"), // 解构结构体样式枚举,直接提取字段 Message::Move { x, y } => println!("移动到: x={}, y={}", x, y), // 解构元组样式枚举,直接提取内部包裹的 String Message::Write(text) => println!("写入内容: {}", text), } } ```